package org.jmeter.tcp.helmet.client;

import com.google.protobuf.InvalidProtocolBufferException;
import com.googlecode.protobuf.format.JsonFormat;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.jmeter.tcp.helmet.protobuf.ServerHelmet;
import org.jmeter.tcp.util.ByteUtils;

import java.io.ByteArrayOutputStream;
import java.util.List;

public class NettyDecoder extends ByteToMessageDecoder {

    private static final Logger log = LoggingManager.getLoggerForClass();

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) {
        // 拆包
        while (byteBuf.isReadable()) {
            internalBuffer().markReaderIndex();
            try {
                ServerHelmet.S2HMessage messageEntity = decodeMessage(byteBuf);


                if (messageEntity == null) {
                    return;
                } else {

                    // 设置返回结果
//                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
//                    messageEntity.writeTo(bos);
//                    String hexResp = ByteUtils.bytesToHex(bos.toByteArray());
//                    MessageContext.setResult(ctx.channel(), hexResp);

                    MessageContext.setResult(ctx.channel(), JsonFormat.printToString(messageEntity));

                    list.add(messageEntity);
                }
            } catch (Exception e) {
                e.printStackTrace();
//                log.error("decode exception", e);
                return;
            }
        }
    }


    /**
     * 头部,80,01010000
     */
    protected static final byte HEAD = 0x50;

    protected static final int LEN_LENGTH = 4;

    private static final int MAX_MESSAGE_LENGTH = 64 * 1024 * 1024;


    public ServerHelmet.S2HMessage decodeMessage(ByteBuf byteBuf) {
        int headerLength = 1;
        byte[] length = new byte[LEN_LENGTH];
        // head
        if (byteBuf.readableBytes() < headerLength) {
            log.warn(String.format("the length for head is not enough, reset reader index"));
            byteBuf.resetReaderIndex();
            return null;
        }
        byte head = byteBuf.readByte();
        if (HEAD != head) {
            log.warn(String.format("invalid head, head is %s", head));
            byteBuf.skipBytes(byteBuf.readableBytes());
            return null;
        }

        // len
        if (byteBuf.readableBytes() < LEN_LENGTH) {
            log.warn(String.format("the length for len is not enough, reset reader index"));
            byteBuf.resetReaderIndex();
            return null;
        }
        byteBuf.readBytes(length);
        // 高配解析时是倒序
        int len = ByteUtils.bytes2Int(ByteUtils.reverse(length));

        // content
        if (byteBuf.readableBytes() < len) {
            log.warn(String.format("the length for content is not enough, reset reader index"));
            byteBuf.resetReaderIndex();
            return null;
        }
        if (len > MAX_MESSAGE_LENGTH) {
            log.warn("high device invalid message length {}" + len);
            byteBuf.skipBytes(byteBuf.readableBytes());
            return null;
        }

        byte[] content;
        content = new byte[len];
        byteBuf.readBytes(content);

        ServerHelmet.S2HMessage message = null;
        try {
            message = ServerHelmet.S2HMessage.parseFrom(content);
            int messageId = message.getMsgid().getNumber();
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
            log.error("high message parse exception", e);
        }
        return message;
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) {
        log.info(String.format("decoder channel unregistered, channel long id: %s, channel short id: %s", ctx.channel().id().asLongText(), ctx.channel().id().asShortText()));
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info(String.format("decoder channel inactive, channel long id: %s, channel short id: %s", ctx.channel().id().asLongText(), ctx.channel().id().asShortText()));
    }
}